﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using NUnit.Framework.Constraints;
using System.Reflection;

namespace NSsh.Common.Tests
{
    public class Throws : Constraint
    {
        public static Throws Exception<TException>() where TException : Exception
        {
            return new Throws(typeof(TException));
        }

        public Throws(Type exceptionType)
        {
            if (!typeof(Exception).IsAssignableFrom(exceptionType))
            {
                throw new ArgumentException("Exception type must be derived from Exception", "exceptionType");
            }

            _exceptionType = exceptionType;
        }

        private Type _exceptionType;
        private Type _actualType;

        public override bool Matches(object actual)
        {
            Delegate action = actual as Delegate;

            if (action == null) throw new ArgumentException("This constraint requires a delegate to execte", "actual");

            try
            {
                action.DynamicInvoke(null);
            }
            catch (TargetInvocationException e)
            {
                if (e.InnerException.GetType() == _exceptionType)
                {
                    return true;
                }
                else
                {
                    _actualType = e.InnerException.GetType();
                    return false;
                }
            }

            return false;
        }

        [CoverageExclude]
        public override void WriteDescriptionTo(NUnit.Framework.MessageWriter writer)
        {
            writer.WriteMessageLine("{0} exception", _exceptionType);
        }

        [CoverageExclude]
        public override void WriteActualValueTo(NUnit.Framework.MessageWriter writer)
        {
            if (_actualType == null)
            {
                writer.WriteMessageLine("no exception was thrown", _actualType);
            }
            else
            {
                writer.WriteMessageLine("{0} exception", _actualType);
            }
        }
    }
}
